#include "stdafx.h"
#include <Thunk/ThisToCdecl.h>

#define THUNK_MACHINE_CODE_IMPLEMENT
#include <Thunk/MachineCodeMacro.h>

namespace Thunk
{
	void __declspec( naked ) ThisToCdecl::Hook()
	{
		_asm
		{
			POP EAX			//1
		// p=&m_method; &m_this=p+4; &oldESP=p+9; &oldRet=p+14
		
		// Save ESP
			//MOV ECX,ESP		//2
			//ADD ECX,4		//3
			//MOV DWORD PTR [EAX+9],ECX	//3
			MOV DWORD PTR [EAX+9],ESP	//3
			ADD DWORD PTR [EAX+9],4		//4

		// Save CallerReturn(by offset)
			//src=&JMP=p+13,dst=CallerReturn,offset=CallerReturn-p-13-5
			MOV ECX,DWORD PTR [ESP]		//3
			SUB ECX,EAX					//2
			SUB ECX,18					//3
			MOV DWORD PTR [EAX+14],ECX	//3

		// Set CalleeReturn
			//MOV ECX,EAX					//2
			//ADD ECX,8					//3
			//MOV DWORD PTR [ESP],ECX		//3
			MOV DWORD PTR [ESP],EAX		//3
			ADD DWORD PTR [ESP],8		//4

		// Set m_this
			MOV ECX,DWORD PTR [EAX+4]	//3

		// Jump to m_method
			JMP DWORD PTR [EAX]			//2
		}
	}
	
	dword_ptr ThisToCdecl::GetObject() const
	{
		return m_this;
	}

	void ThisToCdecl::SetObject(dword_ptr newObj)
	{
		m_this = newObj;
		Helper::FlushInstructionCache(&m_this,sizeof(m_this));
	}

	dword ThisToCdecl::GetMethod() const
	{
		return m_method;
	}

	void ThisToCdecl::SetMethod(dword newMethod)
	{
		m_method = newMethod;
		Helper::ReviseVirtualProtect((void*)this,sizeof(ThisToCdecl));
		Helper::FlushInstructionCache(&m_method,sizeof(m_method));
	}

	ThisToCdecl::~ThisToCdecl()
	{}

	ThisToCdecl::ThisToCdecl(dword_ptr Obj,dword method)
		THIS_TO_CDECL_CODES()
	{
		Helper::SetTransferDST(&CALL,Helper::PointerToInt32(Hook));
		SetObject(Obj);
		SetMethod(method);
	}

	ThisToCdecl::ThisToCdecl(const ThisToCdecl &src)
		THIS_TO_CDECL_CODES()
	{
		Helper::SetTransferDST(&CALL,Helper::PointerToInt32(Hook));
		*this = src;
	}

	ThisToCdecl& ThisToCdecl::operator = (const ThisToCdecl &rhs)
	{
		SetObject( rhs.GetObject() );
		SetMethod( rhs.GetMethod() );
		return *this;
	}

	bool ThisToCdecl::operator == (const ThisToCdecl &rhs) const
	{
		return ( GetObject()==rhs.GetObject() && GetMethod()==rhs.GetMethod() );
	}

	bool ThisToCdecl::operator != (const ThisToCdecl &rhs) const
	{
		return !(*this==rhs);
	}

	dword_ptr ThisToCdecl::Attach(dword_ptr newObj)
	{
		dword_ptr oldObj = GetObject();
		SetObject(newObj);
		return oldObj;
	}

	dword ThisToCdecl::Attach(dword newMethod)
	{
		dword oldMethod = GetMethod();
		SetMethod(newMethod);
		return oldMethod;
	}
}
